﻿using IRUM.TR;
using IRUM.Utils;
using IRUM.XingAPI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Windows.Forms;

namespace IRUM.UI
{
    public partial class FormOldDataMiner : Form
    {
        ThreadOldDataMiner thread = null;
        ThreadStart tStart = null;
        Thread threadOld = null;
        private SessionCtrl m_session = new SessionCtrl();

        public FormOldDataMiner()
        {
            InitializeComponent();
        }

        private void FormOldDataMiner_Load(object sender, EventArgs e)
        {
            LoginDialog dlg = new LoginDialog(m_session);

            if (dlg.ShowDialog() != System.Windows.Forms.DialogResult.OK)
            {
                this.Close();
            }

            thread = new ThreadOldDataMiner(this);
        }

        private void FormOldDataMiner_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (threadOld != null)
            {
                threadOld.Abort();
                threadOld = null;
                tStart = null;
                thread = null;
            }
        }

        private void btnQuery_Click(object sender, EventArgs e)
        {
            pbTotal.Value = 0;

            // 조회기간 설정 199902 ~ 201303
            DateTime sDate = new DateTime(1999, 2, 1);
            DateTime eDate = new DateTime(2001, 1, 1);  //조회할 마지막 달의 다음달로 잡아야 전월만기일 가져올 때 마지막 달 부터 넣을 수 있음.
            thread.SetQueryInfo(sDate, eDate);

            //Thread 시작
            tStart = new ThreadStart(thread.Start);
            threadOld = new Thread(tStart);
            threadOld.Start();
        }

        private delegate void ProgressDisplay(int value);

        public void SetProgressTotal(int value)
        {
            if (pbTotal.InvokeRequired)
            {
                ProgressDisplay printer = new ProgressDisplay(SetProgressTotal);
                this.Invoke(printer, new object[] { value });
            }
            else
            {
                pbTotal.Value = value;
            }
        }

        private delegate void LogDisplay(string log);

        public void SetLog(string log)
        {
            if (rtLog.InvokeRequired)
            {
                LogDisplay printer = new LogDisplay(SetLog);
                this.Invoke(printer, new object[] { log });
            }
            else
            {
                rtLog.Text = DateTime.Now.ToString("hh:mm:dd ") + log + "\r\n" + rtLog.Text;
            }
        }

    }

    /// <summary>
    /// 오래된 차트데이터 수집
    ///       t8427 선물옵션 과거데이터 시간대별 조회 1999년 2월 이후의 데이터 조회 가능
    /// </summary>
    class ThreadOldDataMiner
    {
        FormOldDataMiner form = null;
        Dictionary<int, string> dicYearMatrix = new Dictionary<int, string>();
        Dictionary<int, string> dicMonthMatrix = new Dictionary<int, string>();

        DateTime EndDate;
        int totalMonthCount = 0;
        Queue<DateTime> qCloseDate = new Queue<DateTime>();
        string _expireDate = string.Empty;

        string _FutureCode = string.Empty;
        string _OptionCode = string.Empty;
        string _OptionName = string.Empty;
        float _basePrice = 0;
        float _currPrice = 0;
        bool _isCall = true;
        bool _isUp = true;

        t8416 futureDaily = null;
        t8415 futureMinute = null;
        t8416 optionDaily = null;
        t8415 optionMinute = null;



        public ThreadOldDataMiner(FormOldDataMiner form)
        {
            this.form = form;
            MakeYearMatrix();
            MakeMonthMatrix();
        }

        /// <summary>
        /// 종목코드의 년도별 코드를 준비한다.
        /// </summary>
        private void MakeYearMatrix()
        {
            dicYearMatrix.Add(1996, "6");
            dicYearMatrix.Add(1997, "7");
            dicYearMatrix.Add(1998, "8");
            dicYearMatrix.Add(1999, "9");
            dicYearMatrix.Add(2000, "0");
            dicYearMatrix.Add(2001, "1");
            dicYearMatrix.Add(2002, "2");
            dicYearMatrix.Add(2003, "3");
            dicYearMatrix.Add(2004, "4");
            dicYearMatrix.Add(2005, "5");
            dicYearMatrix.Add(2006, "A");
            dicYearMatrix.Add(2007, "B");
            dicYearMatrix.Add(2008, "C");
            dicYearMatrix.Add(2009, "D");
            dicYearMatrix.Add(2010, "E");
            dicYearMatrix.Add(2011, "F");
            dicYearMatrix.Add(2012, "G");
            dicYearMatrix.Add(2013, "H");
            dicYearMatrix.Add(2014, "J");
            dicYearMatrix.Add(2015, "K");
            dicYearMatrix.Add(2016, "L");
            dicYearMatrix.Add(2017, "M");
            dicYearMatrix.Add(2018, "N");
            dicYearMatrix.Add(2019, "P");
            dicYearMatrix.Add(2020, "Q");
            dicYearMatrix.Add(2021, "R");
            dicYearMatrix.Add(2022, "S");
            dicYearMatrix.Add(2023, "T");
            dicYearMatrix.Add(2024, "V");
            dicYearMatrix.Add(2025, "W");
            dicYearMatrix.Add(2026, "6");
            dicYearMatrix.Add(2027, "7");
            dicYearMatrix.Add(2028, "8");
            dicYearMatrix.Add(2029, "9");
            dicYearMatrix.Add(2030, "0");
            dicYearMatrix.Add(2031, "1");
            dicYearMatrix.Add(2032, "2");
            dicYearMatrix.Add(2033, "3");
            dicYearMatrix.Add(2034, "4");
            dicYearMatrix.Add(2035, "5");
        }

        /// <summary>
        /// 종목코드의 월별 코드를 준비한다.
        /// </summary>
        private void MakeMonthMatrix()
        {
            dicMonthMatrix.Add(1, "1");
            dicMonthMatrix.Add(2, "2");
            dicMonthMatrix.Add(3, "3");
            dicMonthMatrix.Add(4, "4");
            dicMonthMatrix.Add(5, "5");
            dicMonthMatrix.Add(6, "6");
            dicMonthMatrix.Add(7, "7");
            dicMonthMatrix.Add(8, "8");
            dicMonthMatrix.Add(9, "9");
            dicMonthMatrix.Add(10, "A");
            dicMonthMatrix.Add(11, "B");
            dicMonthMatrix.Add(12, "C");
        }

        /// <summary>
        /// 전월의 만기일을 구한다.
        /// </summary>
        /// <param name="curCloseDate"></param>
        /// <returns></returns>
        private DateTime GetPrevCloseDate(DateTime curCloseDate)
        {
            DateTime prevCloseDate = curCloseDate.AddDays(1 - curCloseDate.Day).AddMonths(-1);

            int thursdayCount = 0;

            while (thursdayCount < 2)
            {
                if (prevCloseDate.DayOfWeek == DayOfWeek.Thursday)
                {
                    thursdayCount++;
                }

                if (thursdayCount < 2)
                {
                    prevCloseDate = prevCloseDate.AddDays(1);
                }
            }

            return prevCloseDate;
        }

        /// <summary>
        /// 선물코드 생성
        /// - 해당 월이 선물 만기월인 경우에만 코드 생성되며
        ///   코드 생성과 함께 DB에 저장함.
        /// - 선물코드가 ""인 경우 조회하지 않도록 해야함.
        /// </summary>
        /// <param name="EndDate"></param>
        private void MakeFutureCode()
        {
            if (EndDate.Month % 3 == 0)
            {
                string kind = "F";
                string fcode = "101" + dicYearMatrix[EndDate.Year] + dicMonthMatrix[EndDate.Month] + "000";
                string expire = EndDate.ToString("yyyyMM");
                string hname = "선물 " + EndDate.Month.ToString("0#") + "월물";
                string expcode = "";

                form.prodCodeTableAdapter.sp_ProdCode_i(kind, fcode, hname, expcode, expire);
                form.prodCodeBindingSource.EndEdit();
                form.tableAdapterManager.UpdateAll(form.iRUMDataSet);

                _FutureCode = fcode;
            }
            else
            {
                _FutureCode = string.Empty;
            }
        }

        /// <summary>
        /// 옵션코드 생성
        /// </summary>
        /// <param name="EndDate">만기일</param>
        /// <param name="price">행사가</param>
        /// <param name="isCall">콜/풋 구분</param>
        private void MakeOptionCode()
        {
            //콜
            if (_isCall == true)
            {
                _OptionCode = "201" + dicYearMatrix[EndDate.Year] + dicMonthMatrix[EndDate.Month] + Math.Truncate(_currPrice).ToString("00#");
                _OptionName = "콜 " + EndDate.Month.ToString("0#") + "월 " + _currPrice.ToString("F01");
            }
            else
            {
                _OptionCode = "301" + dicYearMatrix[EndDate.Year] + dicMonthMatrix[EndDate.Month] + Math.Truncate(_currPrice).ToString("00#");
                _OptionName = "풋 " + EndDate.Month.ToString("0#") + "월 " + _currPrice.ToString("F01");
            }
        }

        /// <summary>
        /// 만기일자별 기준가를 설정한다.
        /// </summary>
        private void MakeOptionBasePrice()
        {
            IRUMDataClassesDataContext dc = new IRUMDataClassesDataContext();
            _expireDate = EndDate.ToString("yyyyMMdd");
            string codeFuture = "99999999";
            double price = 0;

            if (dc.ChartDataDays.Where(p => p.gubun.Equals("D") && p.shcode.Equals(codeFuture) && Convert.ToInt64(p.date) <= Convert.ToInt64(_expireDate)).Count() > 0)
            {
                IQueryable<ChartDataDay> query = dc.ChartDataDays.Where(p => p.gubun.Equals("D") && p.shcode.Equals(codeFuture) && Convert.ToInt64(p.date) <= Convert.ToInt64(_expireDate)).Select(p => p);
                _expireDate = query.Select(p => p.date).ToList().Max();

                price = query.Where(p => p.date.Equals(_expireDate)).Select(p => p.close).ToList().Max();

                _basePrice = Convert.ToInt32(price);
                _basePrice -= _basePrice % 5;
                _currPrice = _basePrice;
            }
        }

        /// <summary>
        /// 옵션코드 DB저장
        /// - 옵션코드에 대해서 차트정보 조회결과 데이터가 있을 경우에만 저장함.
        /// </summary>
        private void SaveDBOptionCodeInfo()
        {
            string kind = "O";
            string expire = EndDate.ToString("yyyyMM");
            string expcode = "";

            form.prodCodeTableAdapter.sp_ProdCode_i(kind, _OptionCode, _OptionName, expcode, expire);
            
            //DB저장
            form.prodCodeBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }

        /// <summary>
        /// 조회 기본정보 생성
        /// </summary>
        /// <param name="sDate">조회기간의 시작(1999.2.1)</param>
        /// <param name="eDate">조회기간의 종료 - 조회할 마지막 달의 다음달로 잡아야 전월만기일 가져올 때 마지막 달 부터 넣을 수 있음.</param>
        public void SetQueryInfo(DateTime sDate, DateTime eDate)
        {
            DateTime cDate = GetPrevCloseDate(eDate);

            while (cDate > sDate)
            {
                qCloseDate.Enqueue(cDate);
                cDate = GetPrevCloseDate(cDate);
            }

            totalMonthCount = qCloseDate.Count;
        }

        public void Start()
        {
            EndDate = qCloseDate.Dequeue();
            MakeFutureCode();
            MakeOptionBasePrice();
            MakeOptionCode();

            //선물 일봉조회 -> 옵션 일봉조회
            QueryFutureDaily();

            ////선물 분봉조회 -> 옵션 분봉조회 -- 해당 조회기간동안에는 분봉정보가 없음
            //QueryFutureMinute();
        }


        #region 선물 차트조회
        /// <summary>
        /// 선물 분봉조회
        /// </summary>
        private void QueryFutureMinute()
        {
            if (_FutureCode.Length > 0)
            {
                string codeFuture = _FutureCode;

                Thread.Sleep(1000);
                futureMinute = null;
                futureMinute = new t8415(codeFuture, _expireDate);
                futureMinute.OnReceived += futureMinute_OnReceived;
                futureMinute.Request();
                form.SetLog(">>선물 분봉조회[" + _FutureCode + "]");
            }
            else
            {
                QueryOptionMinute();
            }
        }

        void futureMinute_OnReceived(object s, ChartDataArgs e)
        {
            foreach (ChartData c in e.RetData)
            {
                form.chartDataDayTableAdapter.sp_ChartDataDay_i(c.shcode, c.gubun, c.date, c.time, c.open, c.high, c.low, c.close, c.jdiff_vol, c.openyak);
            }

            form.SetLog("<<선물 분봉수신[" + _FutureCode + "]");

            e.RetData.Clear();
            chartDataDayBindingNavigatorSaveItem();
            QueryOptionMinute();
        }

        /// <summary>
        /// 선물 일봉조회
        /// - 선물종목코드가 없으면 바로 옵션 일봉조회
        /// </summary>
        private void QueryFutureDaily()
        {
            if (_FutureCode.Length > 0)
            {
                string codeFuture = _FutureCode;

                Thread.Sleep(1000);
                futureDaily = null;
                futureDaily = new t8416(codeFuture, _expireDate);
                futureDaily.OnReceived += FutureDaily_OnReceived;
                futureDaily.Request();
                form.SetLog(">>선물 일봉조회[" + _FutureCode + "]");
            }
            else
            {
                QueryOptionDaily();
            }
        }

        void FutureDaily_OnReceived(object s, ChartDataArgs e)
        {
            foreach (ChartData c in e.RetData)
            {
                form.chartDataDayTableAdapter.sp_ChartDataDay_i(c.shcode, c.gubun, c.date, c.time, c.open, c.high, c.low, c.close, c.jdiff_vol, c.openyak);
            }

            form.SetLog("<<선물 일봉수신[" + _FutureCode + "]");

            e.RetData.Clear();
            chartDataDayBindingNavigatorSaveItem();
            QueryOptionDaily();
        }
        #endregion


        #region 옵션차트 조회
        /// <summary>
        /// 옵션 분봉조회
        /// </summary>
        private void QueryOptionMinute()
        {
            string codeOption = _OptionCode;

            Thread.Sleep(1000);
            optionMinute = null;
            optionMinute = new t8415(codeOption, _expireDate);
            optionMinute.OnReceived += optionMinute_OnReceived;
            optionMinute.Request();

            form.SetLog(">>옵션 분봉조회[" + _OptionCode + "]");
        }

        void optionMinute_OnReceived(object s, ChartDataArgs e)
        {
            foreach (ChartData c in e.RetData)
            {
                form.chartDataDayTableAdapter.sp_ChartDataDay_i(c.shcode, c.gubun, c.date, c.time, c.open, c.high, c.low, c.close, c.jdiff_vol, c.openyak);
            }

            form.SetLog("<<옵션 분봉수신[" + _OptionCode + "]");

            if (e.RetData.Count > 0)
            {
                e.RetData.Clear();
                QueryOptionMinute();
            }
            else
            {
                chartDataDayBindingNavigatorSaveItem();
            }
        }

        /// <summary>
        /// 옵션 일봉조회
        /// </summary>
        private void QueryOptionDaily()
        {
            string codeOption = _OptionCode;

            Thread.Sleep(1000);
            optionDaily = null;
            optionDaily = new t8416(codeOption, _expireDate);
            optionDaily.OnReceived += OptionDaily_OnReceived;
            optionDaily.Request();
            form.SetLog(">>옵션 일봉조회[" + _OptionCode + "]");
        }

        void OptionDaily_OnReceived(object s, ChartDataArgs e)
        {
            foreach (ChartData c in e.RetData)
            {
                form.chartDataDayTableAdapter.sp_ChartDataDay_i(c.shcode, c.gubun, c.date, c.time, c.open, c.high, c.low, c.close, c.jdiff_vol, c.openyak);
            }

            form.SetLog("<<옵션 일봉수신[" + _OptionCode + "]");

            //데이터를 DB에 반영한 후 추가작업을 시작한다.
            chartDataDayBindingNavigatorSaveItem();

            // 조회결과 데이터가 있을 경우 방향대로 한단계 더 먼 행사가를 조회한다.
            if (e.RetData.Count > 0)    
            {
                //종목코드를 DB에 추가한다.
                SaveDBOptionCodeInfo();
                e.RetData.Clear();

                if (_isUp == true)
                {
                    _currPrice = _currPrice + 2.5f;
                }
                else
                {
                    _currPrice = _currPrice - 2.5f;
                }

                MakeOptionCode();
                QueryOptionDaily();
            }
            else
            {
                //조회결과가 없을 경우 방향이 위였으면 아래방향으로 전환한다.
                if (_isUp == true)
                {
                    _isUp = false;
                    _currPrice = _basePrice - 2.5f;
                    MakeOptionCode();
                    QueryOptionDaily();
                }
                else
                {
                    //방향이 아래였을 경우 Call을 조회했었다면 Put으로 전환한다.
                    if (_isCall == true)
                    {
                        _isCall = false;
                        _isUp = true;
                        _currPrice = _basePrice;
                        MakeOptionCode();
                        QueryOptionDaily();
                    }
                    else
                    {
                        //Put의 아래방향으로 조회해서 결과가 없을 경우 전월로 이동한다.
                        form.SetProgressTotal(Convert.ToInt32(Convert.ToSingle(totalMonthCount - qCloseDate.Count) / totalMonthCount * 100));
                        
                        if (qCloseDate.Count > 0)
                        {
                            _isCall = true;
                            _isUp = true;
                            EndDate = qCloseDate.Dequeue();
                            MakeFutureCode();
                            MakeOptionBasePrice();
                            MakeOptionCode();
                            QueryFutureDaily(); //선물 일봉조회 후 옵션 일봉조회
                        }
                    }
                }
            }
        }
        #endregion




        #region DB 반영
        /// <summary>
        /// 선물옵션차트데이터 저장
        /// </summary>
        private void chartDataDayBindingNavigatorSaveItem()
        {
            form.chartDataDayBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }

        /// <summary>
        /// 수급데이터 저장
        /// </summary>
        private void investorTradeBindingNavigatorSaveItem()
        {
            form.investorTradeBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }

        /// <summary>
        /// 미결제약정데이터 저장
        /// </summary>
        private void OpenYakBindingNavigatorSaveItem()
        {
            form.openYakBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }

        /// <summary>
        /// 업종차트데이터 저장
        /// </summary>
        private void chartDataMarketBindingNavigatorSaveItem()
        {
            form.chartDataMarketBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }

        /// <summary>
        /// 시가총액상위데이터 저장
        /// </summary>
        private void topStockBindingNavigatorSaveItem()
        {
            form.topStockBindingSource.EndEdit();
            form.tableAdapterManager.UpdateAll(form.iRUMDataSet);
        }
        #endregion
    }
}
